package com.game.framework.spring.jdbctemplate;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport;
import org.springframework.jdbc.core.simple.SimpleJdbcInsert;

/**
 * AbstractJdbcTemplate<br>
 * 说明：<br>
 * 为了更方便使用SimpleJdbcDaoSupport，建议所有DAO继承此类.<br>
 * 如果AbstractJdbcTemplate提供的方法不能满足需求，再使用SimpleJdbcDaoSupport原生的方法.<br>
 * 示例参考：<br>
 * AbstractJdbcTemplateExample<br>
 * SimpleJdbcTemplateExample
 * @author Liyuan
 */

public abstract class AbstractJdbcTemplate<Entity, ID extends Serializable> 
		extends SimpleJdbcDaoSupport
		implements BaseJdbcTemplateDAO<Entity, ID>, BaseDAO<Entity, ID> {
	
	/** Entity's class */
	protected Class<Entity> clazz;
	/** tableName */
	protected String tableName = "";
	/** pkColumnName */
	protected String pkColumnName = "";
	/** autoIncrement */
	protected int autoIncrement = 0;
	
	/** Default Constructor */
	@SuppressWarnings("unchecked")
	public AbstractJdbcTemplate(){
		Class c = getClass();
        Type t = c.getGenericSuperclass();
        if (t instanceof ParameterizedType) {
            Type[] p = ((ParameterizedType) t).getActualTypeArguments();
            this.clazz = (Class<Entity>) p[0];
        }
	}
	
	// setter
	public void setTableName(String tableName) {
		this.tableName = tableName;
	}

	public void setPkColumnName(String pkColumnName) {
		this.pkColumnName = pkColumnName;
	}
	
	public void setAutoIncrement(int autoIncrement) {
		this.autoIncrement = autoIncrement;
	}

	// implements by BaseJdbcTemplateDAO
	/**
	 * execute
	 * @param sql
	 * @param args
	 * @return
	 */
	public int execute(String sql, Object... args){
		return super.getSimpleJdbcTemplate().update(sql, args);
	}
	
	/**
	 * execute
	 * @param sql
	 * @param entity
	 * @return
	 */
	public int execute(String sql, Entity entity){
		return super.getSimpleJdbcTemplate().update(sql, new BeanPropertySqlParameterSource(entity));
	}
	
	/**
	 * save
	 * @param entity
	 * @param tableName
	 * @param pkColumnName
	 * @return ID
	 */
	public Number save(Entity entity, String tableName, String pkColumnName){
		SimpleJdbcInsert insertActor = 
				new SimpleJdbcInsert(this.getDataSource())
				.withTableName(tableName).usingGeneratedKeyColumns(pkColumnName);
		return insertActor.executeAndReturnKey(new BeanPropertySqlParameterSource(entity));
	}
	
	/**
	 * add
	 * @param sql
	 * @param entity
	 * @return
	 */
	public int add(String sql, Entity entity){
		return execute(sql, entity);
	}
	
	/**
	 * add
	 * @param sql
	 * @param args
	 * @return
	 */
	public int add(String sql, Object... args){
		return execute(sql, args);
	}

	/**
	 * update
	 * @param sql
	 * @param entity
	 * @return
	 */
	public int update(String sql, Entity entity){
		return execute(sql, entity);
	}
	
	/**
	 * update
	 * @param sql
	 * @param args
	 * @return
	 */
	public int update(String sql, Object... args){
		return execute(sql, args);
	}
	
	/**
	 * truncate
	 * @param tableName
	 */
	public void truncate(String tableName){
		String sql = "truncate " + tableName;
		super.getSimpleJdbcTemplate().update(sql);
	}
	
	/**
	 * delete
	 * @param sql
	 * @param entity
	 * @return
	 */
	public int delete(String sql, Object... objs){
		return execute(sql, objs);
	}
	
	/**
	 * delete
	 * @param sql
	 * @param entity
	 * @return
	 */
	public int delete(String sql, Entity entity){
		return execute(sql, entity);
	}
	
	/**
	 * count
	 * @param sql
	 * @param args
	 * @return
	 */
	public long count(String sql, Object... args){
		return super.getSimpleJdbcTemplate().queryForLong(sql, args);
	}
	
	/**
	 * countByPrimaryKey
	 * @param id
	 * @return
	 */
	public long countByPrimaryKey(ID id){
		String SQL_COUNT_BY_ID = "select count(*) from " + this.tableName + " where " + this.pkColumnName + " = ?";
		long count = super.getSimpleJdbcTemplate().queryForLong(SQL_COUNT_BY_ID, id);
		return count;
	}
	
	/** get */
	public Entity get(String sql, Object... args){
//		return super.getSimpleJdbcTemplate().<Entity> queryForObject(
//				sql, new BeanPropertyRowMapper<Entity>(clazz), args);
    	List<Entity> list = find(sql, args);
    	return list == null || list.size() == 0 ? null : list.get(0);
	}
	
	/**
	 * find
	 * @param sql
	 * @param args
	 * @return
	 */
	public List<Entity> find(String sql, Object... args){
    	return super.getSimpleJdbcTemplate().query(
    			sql, new BeanPropertyRowMapper<Entity>(clazz), args);
	}
	
	// implements by BaseDAO
	/**
	 * save
	 * @param entity
	 * @return
	 */
	public Number save(Entity entity){
		return this.save(entity, tableName, pkColumnName);
	}
	
	/**
	 * saveOrUpdate
	 * @param entity
	 * @param id
	 * @return
	 */
	public Number saveOrUpdate(Entity entity, ID id){
		Number rs = 0;
		long count = this.countByPrimaryKey(id);
		if(count > 0){ 
			rs = this.update(entity);
			return rs;
		}
		if(this.autoIncrement == 1){
			rs = this.save(entity);
		} else {
			rs = this.add(entity);
		}
		return rs;
	}
	
	/**
	 * add: 使用该方法时需要实现
	 * @param entity
	 */
	public int add(Entity entity){
		return 0;
	}
	
	/**
	 * update：使用该方法时需要实现
	 * @param entity
	 * @return
	 */
	public int update(Entity entity){
		return 0;
	}
	
	/**
	 * delete
	 * @param id
	 * @return
	 */
	public int delete(ID id){
		String SQL_DELETE_BY_ID = "delete from " + tableName + " where " + pkColumnName + " = ?";
		return this.execute(SQL_DELETE_BY_ID, id);
	}
	
	/**
	 * truncate
	 */
	public void truncate(){
		this.truncate(tableName);
	}
	
	/**
	 * get
	 * @param id
	 * @return
	 */
	public Entity get(ID id){
		String SQL_GET_BY_PK = "select * from " + tableName + " where " + pkColumnName + " = ?";
		return this.get(SQL_GET_BY_PK, id);
	}
	
	/**
	 * find
	 * @return
	 */
	public List<Entity> find(){
		String SQL_FIND_ALL = "select * from " + tableName;
		return this.find(SQL_FIND_ALL);
	}
	
	/**
	 * count
	 * @return
	 */
	public long count(){
		String SQL_COUNT_ALL = "select count(*) from " + tableName;
		return this.count(SQL_COUNT_ALL);
	}
	
	/**
	 * getMaxId
	 * @return
	 */
	public long getMaxId(){
		String SQL_GET_MAX_ID = "select max(" + pkColumnName + ") from " + tableName;
		return super.getSimpleJdbcTemplate().queryForLong(SQL_GET_MAX_ID);
	}
	
	/**
	 * getMaxId
	 * @return
	 */
	public Integer getMaxIntId(){
		String SQL_GET_MAX_ID = "select max(" + pkColumnName + ") from " + tableName;
		return super.getSimpleJdbcTemplate().queryForInt(SQL_GET_MAX_ID);
	}
	
}
